#include "bs_pull_streamer.h"
#include<opencv2/opencv.hpp>


// char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };
// #define av_err2str(errnum) av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, errnum)

BsPullStreamer::BsPullStreamer()
{
   
}

BsPullStreamer::~BsPullStreamer()
{
    mThread->join();
    mThread = nullptr;
}


bool BsPullStreamer::setup(std::string name, std::string decoder)
{
    this->name = name;
    this->decoder = decoder;

    if (!connect())
    {
        std::cout << "BsPullStreamer::setup error\n";
        return false;
    }

    mVideoFrame = new VideoFrame(VideoFrame::BGR, width, height);
    std::cout << "BsStreamer::setup Success!\n";
    start();
    return true;
}

void BsPullStreamer::start()
{
    mThread = new std::thread(BsPullStreamer::decodeVideoAndShowThread, this);
    mThread->native_handle();
    pull_running = true;
}


bool BsPullStreamer::connect()
{
    mFmtCtx = avformat_alloc_context();

    AVDictionary* fmt_options = NULL;
    av_dict_set(&fmt_options, "rtsp_transport", "tcp", 0); //设置rtsp底层网络协议 tcp or udp
    av_dict_set(&fmt_options, "stimeout", "3000000", 0);   //设置rtsp连接超时（单位 us）
    av_dict_set(&fmt_options, "rw_timeout", "3000000", 0); //设置rtmp/http-flv连接超时（单位 us）


    int ret = avformat_open_input(&mFmtCtx,  this->name.c_str(), NULL, &fmt_options);
    if (ret != 0) {
        std::cout <<  "avformat_open_input error: url = " <<  this->name << std::endl;
        return false;
    }
    if (avformat_find_stream_info(mFmtCtx, NULL) < 0) {
        std::cout << "avformat_find_stream_info error" << std::endl;
        return false;
    }

    for (int i = 0; i < mFmtCtx->nb_streams; i++)
    {
        if (mFmtCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            mVideoIndex = i;
            break;
        }
    }

    if(mVideoIndex <= -1)
    {
        std::cout << "av_find_best_stream video error videoIndex=" << mVideoIndex << std::endl;
        return false;
    }

    AVCodecParameters* videoCodecPar = mFmtCtx->streams[mVideoIndex]->codecpar;

    AVCodec* videoCodec = NULL;
    videoCodec = avcodec_find_decoder_by_name("h264_cuvid");// 英伟达独显
    //videoCodec = avcodec_find_decoder_by_name("h264_qsv");// 酷睿核显
    // videoCodec = avcodec_find_decoder(AV_CODEC_ID_H264);


    mVideoCodecCtx = avcodec_alloc_context3(videoCodec);
    if (avcodec_parameters_to_context(mVideoCodecCtx, videoCodecPar) != 0) 
    {
        std::cout << "avcodec_parameters_to_context error" << std::endl;
        return false;
    }
    if (avcodec_open2(mVideoCodecCtx, videoCodec, nullptr) < 0) 
    {
        std::cout << "avcodec_open2 error" << std::endl;
        return false;
    }


    mVideoStream = mFmtCtx->streams[mVideoIndex];
    if (mVideoStream->avg_frame_rate.den == 0) 
    {
        std::cout << "videoStream->avg_frame_rate.den = 0" << std::endl;
        this->fps = 25;
    }
    else
    {
        this->fps = mVideoStream->avg_frame_rate.num / mVideoStream->avg_frame_rate.den;
    }
    // std::cout << "mVideoStream->avg_frame_rate:" << std::endl;
    // std::cout << "mVideoStream->avg_frame_rate.num:" << mVideoStream->avg_frame_rate.num << std::endl;
    // std::cout << "mVideoStream->avg_frame_rate.den:" << mVideoStream->avg_frame_rate.den << std::endl;
    
    this->width = mVideoCodecCtx->width;
    this->height = mVideoCodecCtx->height;

    return true;
}

void BsPullStreamer::decodeVideoAndShowThread(void* arg)
{
    BsPullStreamer *mBsPullStreamer = (BsPullStreamer *)arg;
    
    
    AVPacket pkt;
    int continuity_error_count = 0;


    // pkt->解码->frame
    AVFrame* frame_yuv420p = av_frame_alloc();
    AVFrame* frame_bgr = av_frame_alloc();
    cv::Mat frame_bgr_mat = cv::Mat(mBsPullStreamer->height, mBsPullStreamer->width, CV_8UC3);

    int frame_bgr_buff_size = av_image_get_buffer_size(AV_PIX_FMT_BGR24, mBsPullStreamer->width, mBsPullStreamer->height, 1);
    uint8_t* frame_bgr_buff = (uint8_t*)av_malloc(frame_bgr_buff_size);
    av_image_fill_arrays(
        frame_bgr->data, frame_bgr->linesize, 
        frame_bgr_buff, AV_PIX_FMT_BGR24, 
        mBsPullStreamer->width, mBsPullStreamer->height, 1
    );


    SwsContext* sws_ctx_yuv420p2bgr = sws_getContext(
        mBsPullStreamer->width, mBsPullStreamer->height,
        mBsPullStreamer->mVideoCodecCtx->pix_fmt,
        mBsPullStreamer->mVideoCodecCtx->width,
        mBsPullStreamer->mVideoCodecCtx->height,
        AV_PIX_FMT_BGR24,
        SWS_BICUBIC, nullptr, nullptr, nullptr
    );

    int64_t frameCount = 0;
    auto cnt_time = std::chrono::system_clock::now().time_since_epoch();
    auto last_update_time = std::chrono::system_clock::now().time_since_epoch();

    while (mBsPullStreamer->pull_running)
    {
        // 拉流读取
        if (av_read_frame(mBsPullStreamer->mFmtCtx, &pkt) >= 0) 
        {
            continuity_error_count = 0;

            if (pkt.stream_index == mBsPullStreamer->mVideoIndex) 
            {
                // 获得pkt之后解码为RGB
                if (avcodec_send_packet(mBsPullStreamer->mVideoCodecCtx, &pkt) == 0)
                {
                    frameCount++;
                    // 解码成功
                    avcodec_receive_frame(mBsPullStreamer->mVideoCodecCtx, frame_yuv420p);
                    // frame（yuv420p） 转 frame_bgr
                    sws_scale(
                        sws_ctx_yuv420p2bgr,
                        frame_yuv420p->data, 
                        frame_yuv420p->linesize, 
                        0, mBsPullStreamer->height,
                        frame_bgr->data, frame_bgr->linesize
                    );
                    frame_bgr_mat.data = frame_bgr->data[0];

                    cnt_time = std::chrono::system_clock::now().time_since_epoch();
                    std::cout << fmt::format("[{}] frameCount={} fps={:.2f}\n",
                        std::chrono::duration_cast<std::chrono::milliseconds>(cnt_time).count(),
                        frameCount,
                        (float)1000/(std::chrono::duration_cast<std::chrono::milliseconds>(cnt_time).count()-std::chrono::duration_cast<std::chrono::milliseconds>(last_update_time).count()) 
                    
                    );
                    last_update_time = cnt_time;

                    cv::imshow("rtsp_pull", frame_bgr_mat);
                    cv::waitKey(1);
                }

                std::this_thread::sleep_for(std::chrono::milliseconds(100/mBsPullStreamer->fps));
            }
            else 
            {
                av_packet_unref(&pkt);
            }
        }
        else
        {
            if(continuity_error_count > 500)
            {
                mBsPullStreamer->stop();
                break;
            }

            av_packet_unref(&pkt);
            continuity_error_count++;
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            
        }
        
    }
    std::cout << fmt::format("Pull_running is false!\n");
    std::cout << fmt::format("end stream!\n");

}
